/*
 * Copyright 2010, 2011 Matt Oliveri
 *
 * This file is part of JConfigGen
 *
 * JConfigGen is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JConfigGen is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with JConfigGen.  If not, see <http://www.gnu.org/licenses/>.
 */

package atma.jconfiggen.sax;

import java.util.EnumMap;
import java.util.HashMap;
import java.util.Stack;

import java.io.PrintStream;

import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import org.xml.sax.helpers.DefaultHandler;

import atma.jconfiggen.InvalidConfigException;
import atma.jconfiggen.Config;
import atma.jconfiggen.Proxy;

public final class SAXHandler extends DefaultHandler
{
	static final String nmsp = "atma:JConfigGen";

	static enum Elem
	{
		// Non-Expr elements
		CONFIG,IMPORT,PARAM,
		CONSTRUCTOR,

		// Expr elements
		NOFX,
		CODE,
		SEQ,
		BLOCK,
		BLOCKWITH,
		WITH,
		NEW,
		TARGET,
		CALL,
		CALLOBJ,
		ARRAY
	}

	private static final HashMap<String,Elem> emap = new HashMap<String,Elem>();
	static {for (Elem e:Elem.values()) emap.put(e.name().toLowerCase(),e);}

	static <T> T complain(Locator loc,String msg) throws SAXException {throw new SAXParseException(msg,loc);}

	private final EnumMap<Elem,HFact> fmap = new EnumMap<Elem,HFact>(Elem.class);
	private final Stack<ElemH> hdls = new Stack<ElemH>();

	final Stack<Proxy> withs = new Stack<Proxy>();
	Locator loc;
	Config config;

	public SAXHandler() {}

	HFact getFactory(Elem e) {return fmap.get(e);}

	Proxy getWith() throws SAXException
	{
		if (withs.empty()) complain(loc,"No call target has been established here");
		return withs.peek();
	}

	public Config getConfig() {return config;}

	public void setDocumentLocator(Locator l) {loc = l;}

	public void startDocument()
	{
		fmap.put(Elem.NOFX,new NoFXHFact(this));
		fmap.put(Elem.CODE,new CodeHFact(this));
		fmap.put(Elem.SEQ,new SeqHFact(this));
		fmap.put(Elem.BLOCK,new BlockHFact(this));
		fmap.put(Elem.BLOCKWITH,new BlockWithHFact(this));
		fmap.put(Elem.WITH,new WithHFact(this));
		fmap.put(Elem.NEW,new NewHFact(this));
		fmap.put(Elem.TARGET,new TargetHFact(this));
		fmap.put(Elem.CALL,new CallHFact(this));
		fmap.put(Elem.CALLOBJ,new CallObjHFact(this));
		fmap.put(Elem.ARRAY,new ArrayHFact(this));

		hdls.push(new RootH(this));
	}

	public void startElement(String nsuri,String lname,String qn,Attributes attr) throws SAXException
	{
		if (nsuri.equals("")) hdls.push(hdls.peek().elemstr(lname,attr));
		else if (nsuri.equals(nmsp))
		{
			Elem e = emap.get(lname);
			if (e == null) complain(loc,"Unknown element: " + lname);
			hdls.push(hdls.peek().elem(e,attr));
		}
		else complain(loc,"Unsupported namespace: " + nsuri);
	}

	public void endElement(String ns,String ln,String qn) throws SAXException {hdls.pop().end();}
	public void characters(char[] ch,int s,int l) throws SAXException {hdls.peek().chars(ch,s,l);}
}
